/*
 * Copyright 2013 JROSE
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jrose.test.support;

import lombok.AccessLevel;
import lombok.Cleanup;
import lombok.NoArgsConstructor;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.jdbc.JdbcTestUtils;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Table DDL script manager implementation.
 *
 * @author Xiaohai Zhang
 * @since 2013-08-17 16:25
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
abstract public class TableScriptManager {
    private static final String DDL_BASE_DIR = "/org/jrose/test/ddl/";
    private static final String DDL_RESOURCE = "/org/jrose/test/support/DDL_SCRIPTS.ddl";

    private static final AtomicBoolean initialized = new AtomicBoolean(false);
    private static final Map<String, String> resourceCache = new HashMap<>();
    private static final Map<String, String> databaseCache = new HashMap<>();

    public synchronized static void initialize(DataSource dataSource) {
        Assert.notNull(dataSource);
        if (initialized.get()) {
            return;
        }
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        Resource resource = new ClassPathResource(DDL_RESOURCE);
        JdbcTestUtils.executeSqlScript(jdbcTemplate, resource, false);
        initialized.set(true);
    }

    public synchronized static DDLScript getScript(DataSource dataSource, String tableName) {
        Assert.isTrue(initialized.get());
        Assert.notNull(dataSource);
        Assert.notNull(tableName);

        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        String scriptFromResource = loadScriptFromResource(tableName);
        String scriptFromDatabase = loadScriptFromDatabase(tableName, jdbcTemplate);
        DDLScript script = new DDLScript(jdbcTemplate);
        script.setTableName(tableName);
        script.setScript(scriptFromResource);
        script.setUpToDate(StringUtils.equals(scriptFromResource, scriptFromDatabase));
        return script;
    }

    private static String loadScriptFromResource(String tableName) {
        if (resourceCache.containsKey(tableName)) {
            return resourceCache.get(tableName);
        }
        try {
            String resourcePath = DDL_BASE_DIR + tableName + ".ddl";
            Resource resource = new ClassPathResource(resourcePath);
            @Cleanup InputStream inStream = resource.getInputStream();
            String script = IOUtils.toString(inStream, "UTF-8");
            resourceCache.put(tableName, script);
            return script;
        } catch (IOException ex) {
            throw new IllegalStateException("Cannot find DDL of " + tableName + ", check your test environment");
        }
    }

    private static String loadScriptFromDatabase(String tableName, JdbcTemplate jdbcTemplate) {
        if (databaseCache.containsKey(tableName)) {
            return databaseCache.get(tableName);
        }
        String sql = "SELECT DDL_SCRIPT FROM DDL_SCRIPTS WHERE TABLE_NAME=?";
        List<String> scripts = jdbcTemplate.queryForList(sql, new Object[]{tableName}, String.class);
        if (scripts.isEmpty()) {
            return "";
        }
        String script = scripts.get(0);
        databaseCache.put(tableName, script);
        return script;
    }
}
